package com.zhb.cloud.core.distribute.lock;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @ClassName: DistributedLockUtils
 * @author: zhb
 * @date: 2017年8月3日 下午6:58:32
 * @Description: 分布式所核心工具类
 *
 */
public class DistributedLockUtils {
	private static final Logger LOG = LoggerFactory.getLogger(DistributedLockUtils.class);

	/**
	 * Lock quietly.
	 *
	 * @param lock
	 */
	public static void lockQuietly(DistributedLock lock) {
		if (lock == null)
			return;
		try {
			lock.lock();
		} catch (Exception e) {
			LOG.warn(ExceptionUtils.getStackTrace(e));
		}
	}

	/**
	 * TryLock quietly.
	 *
	 * @param lock
	 */
	public static boolean tryLockQuietly(DistributedLock lock) {
		if (lock == null)
			return false;

		try {
			return lock.tryLock();
		} catch (Exception e) {
			LOG.warn(ExceptionUtils.getStackTrace(e));
		}
		return false;
	}

	/**
	 * TryLock quietly.
	 * 
	 * @param lock
	 * @param time
	 * @param unit
	 * @return
	 */
	public static boolean tryLockQuietly(DistributedLock lock, long time, TimeUnit unit) {
		if (lock == null)
			return false;

		try {
			return lock.tryLock(time, unit);
		} catch (Exception e) {
			LOG.warn(ExceptionUtils.getStackTrace(e));
		}
		return false;
	}

	/**
	 * Unlock quietly.
	 *
	 * @param lock
	 */
	public static void unlockQuietly(DistributedLock lock) {
		if (lock == null)
			return;

		try {
			lock.unlock();
		} catch (Exception e) {
			LOG.warn(ExceptionUtils.getStackTrace(e));
		}
	}

	/**
	 * Validate the provided znode path string
	 * 
	 * @param path
	 *            path string
	 * @throws IllegalArgumentException
	 *             if the path is invalid
	 */
	public static void validatePath(String path) throws IllegalArgumentException {
		if (path == null) {
			throw new IllegalArgumentException("Path cannot be null");
		}
		if (path.length() == 0) {
			throw new IllegalArgumentException("Path length must be > 0");
		}
		if (path.charAt(0) != '/') {
			throw new IllegalArgumentException("Path must start with / character");
		}
		if (path.length() == 1) { // done checking - it's the root
			return;
		}
		if (path.charAt(path.length() - 1) == '/') {
			throw new IllegalArgumentException("Path must not end with / character");
		}

		String reason = null;
		char lastc = '/';
		char chars[] = path.toCharArray();
		char c;
		for (int i = 1; i < chars.length; lastc = chars[i], i++) {
			c = chars[i];

			if (c == 0) {
				reason = "null character not allowed @" + i;
				break;
			} else if (c == '/' && lastc == '/') {
				reason = "empty node name specified @" + i;
				break;
			} else if (c == '.' && lastc == '.') {
				if (chars[i - 2] == '/' && (i + 1 == chars.length || chars[i + 1] == '/')) {
					reason = "relative paths not allowed @" + i;
					break;
				}
			} else if (c == '.') {
				if (chars[i - 1] == '/' && (i + 1 == chars.length || chars[i + 1] == '/')) {
					reason = "relative paths not allowed @" + i;
					break;
				}
			} else if (c > '\u0000' && c < '\u001f' || c > '\u007f' && c < '\u009F' || c > '\ud800' && c < '\uf8ff'
					|| c > '\ufff0' && c < '\uffff') {
				reason = "invalid charater @" + i;
				break;
			}
		}

		if (reason != null) {
			throw new IllegalArgumentException("Invalid path string \"" + path + "\" caused by " + reason);
		}
	}

	/**
	 * Returns localHost's IP address.
	 * 
	 * @return
	 */
	public static String getHostAddress() {
		String ip = null;
		try {
			// InetAddress
			ip = InetAddress.getLocalHost().getHostAddress();

			// NetworkInterface address
			List<String> list = new ArrayList<String>();
			Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
			while (interfaces.hasMoreElements()) {
				NetworkInterface iface = interfaces.nextElement();
				// filters out 127.0.0.1 and inactive interfaces
				if (iface.isLoopback() || !iface.isUp())
					continue;

				Enumeration<InetAddress> addresses = iface.getInetAddresses();
				while (addresses.hasMoreElements()) {
					InetAddress addr = addresses.nextElement();
					list.add(addr.getHostAddress());
				}
			}

			// sort
			Collections.sort(list);
			if (!list.isEmpty()) {
				ip = list.get(0).toString();
			}
		} catch (SocketException | UnknownHostException e) {
			LOG.warn(ExceptionUtils.getStackTrace(e));
		}
		return ip;
	}
}
